#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "functions.c"

WebServer httpServer;
pthread_mutex_t mux = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t work = PTHREAD_COND_INITIALIZER;
pthread_cond_t threads = PTHREAD_COND_INITIALIZER;

void *listenServer(void * arg) {

	int fd, fdClient, n, sin_size;
	struct sockaddr_in server;
	struct sockaddr_in client;

	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		error("Error en Socket Server");
	}
	server.sin_family = AF_INET;
	server.sin_port = htons(PORT);
	server.sin_addr.s_addr = INADDR_ANY;
	bzero(&(server.sin_zero), sizeof(server));

	int reuse = 1;
	setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*) &reuse, sizeof(reuse));

	if (bind(fd, (struct sockaddr*) &server, sizeof(struct sockaddr)) == -1) {
		error("Error en Socket Server (BIND)");
	}

	if (listen(fd, NUMPTHREAD) == -1) {
		error("Error en Socket Server (LISTEN)");
	}

	while (1) {

		sin_size = sizeof(struct sockaddr_in);
		if ((fdClient = accept(fd, (struct sockaddr *) &client, &sin_size)) == -1) {
			error("Error en Socket Server (ACCEPT)");
		}

		pthread_mutex_lock(&mux);
		if (httpServer.numElementos + 1 <= NUMCOLA) {
			httpServer.numElementos = httpServer.numElementos + 1;
			httpServer.peticiones = httpServer.peticiones + 1;
			insertFD(&httpServer, fdClient);
			trace(httpServer, -1, fdClient);
			viewServer(httpServer);
		} else {
			printf("Servidor Lleno\n");
			responseHTTP(fdClient, 1);
			close(fdClient);
		}
		pthread_cond_signal(&work);
		pthread_mutex_unlock(&mux);
	}

	pthread_exit(NULL);
	return EXIT_SUCCESS;
}

void *getElement(void * arg) {
	int threadId = (int) arg;
	int tWait = httpServer.tEspera[threadId];
	printf("Thread: %d Tiempo Espera: %f seg\n", threadId, (float) 1 / tWait);
	while (1) {
		pthread_mutex_lock(&mux);
		pthread_cond_wait(&threads, &mux);
		int fdClient = getFD(&httpServer);
		if (fdClient != -1) {
			usleep(1000000 / tWait);
			httpServer.numElementos = httpServer.numElementos - 1;
			trace(httpServer, threadId, fdClient);
			responseHTTP(fdClient, 0);
			close(fdClient);
			viewServer(httpServer);
		}
		pthread_mutex_unlock(&mux);
	}

	pthread_exit(NULL);
	return EXIT_SUCCESS;
}

void *master(void * arg) {
	while (1) {
		pthread_mutex_lock(&mux);
		pthread_cond_wait(&work, &mux);
		pthread_mutex_unlock(&mux);
		while (httpServer.numElementos > 0) {
			pthread_cond_broadcast(&threads);
		}

	}
	pthread_exit(NULL);
	return EXIT_SUCCESS;
}

int main(int argc, char *argv[]) {

	int i = 1;
	unsigned int iseed = (unsigned int) time(NULL);
	srand(iseed);
	pthread_t serverThread[NUMPTHREAD];
	pthread_t socketThread;
	pthread_t masterThread;

	//Inicializar lista a -1
	initializeServer(&httpServer);

	//Create Threads Socket
	pthread_create(&socketThread, NULL, listenServer, NULL);

	//Create Threads Petición
	for (i = 0; i < NUMPTHREAD; i++) {
		pthread_create(&serverThread[i], NULL, getElement, (void *) i);
	}

	pthread_create(&masterThread, NULL, master, NULL);

	//Join Threads
	for (i = 0; i < NUMPTHREAD; i++) {
		(void) pthread_join(serverThread[i], NULL);
	}
	(void) pthread_join(socketThread, NULL);
	(void) pthread_join(masterThread, NULL);

	return EXIT_SUCCESS;

}
